package controller

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"im/service"
	"im/set-master"
	"log"
	"net/http"
	"strconv"
	"sync"
)

type chat struct{}

func NewChat() *chat {
	return &chat{}
}

func (c *chat) StartInitChat(router *gin.RouterGroup) {
	// 127.0.0.1/chat?id=1&token=xxxxxx
	router.GET("/chat", c.chatWithFriend)
}

func (c *chat) chatWithFriend(context *gin.Context) {
	//todo 获取数据
	stringid := context.Query("id")
	uint64id, err := strconv.ParseUint(stringid, 10, 32)
	userid := uint(uint64id)
	if err != nil {
		log.Println("id转换过程中出现错误！")
		return
	}
	token := context.Query("token")
	// 检查token
	chatservice := new(service.ChatService)
	isValid, err := chatservice.CheckToken(userid, token)
	if err != nil {
		log.Println(err.Error())
		return
	}
	// 验证成功

	// 验证失败
	// 这是将http升级成websocket，返回的conn就是连接的handle
	conn, err := (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return isValid
		},
	}).Upgrade(context.Writer, context.Request, nil)
	if err != nil {
		log.Println("升级socket失败！")
		return
	}
	node := Node{
		Conn: conn,
		// 并行数据 到 处理器这边转成串行的
		DataQueue: make(chan []byte, 50),
		GroupSet:  set.New(set.ThreadSafe),
	}

	//	userid和node形成绑定关系
	// 意思就是前端会有很多的websocket，我们将id对应的conn对应保存起来
	// 之后在读写的时候需要找到这个映射
	rwlocker.Lock() // 这个是读写锁中的写锁
	clientMap[userid] = &node
	rwlocker.Unlock()

	//	 发送逻辑
	go sendproc(&node)

	// todo 接收信息还没有写，只完成了发送消息。
	go recvproc(&node)
	sendMessage(userid, []byte("hello gcc"))
}

type Node struct {
	Conn      *websocket.Conn
	DataQueue chan []byte
	GroupSet  set.Interface
}

// 映射关系表
var clientMap map[uint]*Node = make(map[uint]*Node, 1024)

// 读写锁
var rwlocker sync.RWMutex

// 发送协程
func sendproc(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			if err := node.Conn.WriteMessage(websocket.TextMessage, data); err != nil {
				log.Println("发送信息出错")
				return
			}
		}
	}
}

//接收协程
func recvproc(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			log.Println("接收信息出错！")
			return
		}
		// 对data 进一步的处理
		fmt.Printf("recv<=%s", data)
	}
}

//发送消息
func sendMessage(userid uint, msg []byte) {
	rwlocker.RLock()
	node, ok := clientMap[userid]
	rwlocker.RUnlock()
	if ok {
		node.DataQueue <- msg
	}
}

//检测是否有效
//func checkToken(user uint, token string) bool {
//	// 从数据库里查询并比对---返回值就是bool
//	return
//}
//todo 我的检查token在 service中，所以跟教程不同
